/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Nico Seessle - bug 51332 *******************************************************************************/ package org.eclipse.ant.internal.ui.model; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.Stack; import java.util.regex.Pattern; import org.apache.tools.ant.AntTypeDefinition; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.ComponentHelper; import org.apache.tools.ant.IntrospectionHelper; import org.apache.tools.ant.Location; import org.apache.tools.ant.Main; import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectHelperRepository; import org.apache.tools.ant.RuntimeConfigurable; import org.apache.tools.ant.Target; import org.apache.tools.ant.Task; import org.apache.tools.ant.TaskAdapter; import org.apache.tools.ant.UnknownElement; import org.eclipse.ant.core.AntCorePlugin; import org.eclipse.ant.core.AntCorePreferences; import org.eclipse.ant.core.AntSecurityException; import org.eclipse.ant.core.Property; import org.eclipse.ant.core.Type; import org.eclipse.ant.internal.core.AntClassLoader; import org.eclipse.ant.internal.core.AntCoreUtil; import org.eclipse.ant.internal.core.AntSecurityManager; import org.eclipse.ant.internal.core.IAntCoreConstants; import org.eclipse.ant.internal.ui.AntUIPlugin; import org.eclipse.ant.internal.ui.AntUtil; import org.eclipse.ant.internal.ui.editor.DecayCodeCompletionDataStructuresThread; import org.eclipse.ant.internal.ui.editor.outline.AntEditorMarkerUpdater; import org.eclipse.ant.internal.ui.editor.utils.ProjectHelper; import org.eclipse.ant.internal.ui.preferences.AntEditorPreferenceConstants; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.ISynchronizable; import org.xml.sax.Attributes; import org.xml.sax.SAXParseException; import com.ibm.icu.text.MessageFormat; public class AntModel implements IAntModel { private static ClassLoader fgClassLoader; private static int fgInstanceCount = 0; private static Object loaderLock = new Object(); private IDocument fDocument; private IProblemRequestor fProblemRequestor; private LocationProvider fLocationProvider; private AntProjectNode fProjectNode; private AntTargetNode fCurrentTargetNode; private AntElementNode fLastNode; private AntElementNode fNodeBeingResolved; private int fNodeBeingResolvedIndex = -1; private String fEncoding = null; private Map<String, String> fEntityNameToPath; /** * Stack of still open elements. * <P> * On top of the stack is the innermost element. */ private Stack<AntElementNode> fStillOpenElements = new Stack<>(); private Map<Task, AntTaskNode> fTaskToNode = new HashMap<>(); private List<AntTaskNode> fTaskNodes = new ArrayList<>(); private final Object fDirtyLock = new Object(); private boolean fIsDirty = true; private File fEditedFile = null; private ClassLoader fLocalClassLoader = null; private boolean fHasLexicalInfo = true; private boolean fHasPositionInfo = true; private boolean fHasTaskInfo = true; private IDocumentListener fListener; private AntEditorMarkerUpdater fMarkerUpdater = null; private List<AntElementNode> fNonStructuralNodes = new ArrayList<>(1); private IPreferenceChangeListener fCoreListener = new IPreferenceChangeListener() { @Override public void preferenceChange(PreferenceChangeEvent event) { if (IAntCoreConstants.PREFERENCE_CLASSPATH_CHANGED.equals(event.getKey())) { if (Boolean.parseBoolean((String) event.getNewValue()) == true) { reconcileForPropertyChange(true); } } } }; private IPreferenceChangeListener fUIListener = new IPreferenceChangeListener() { @Override public void preferenceChange(PreferenceChangeEvent event) { String property = event.getKey(); if (property.equals(AntEditorPreferenceConstants.PROBLEM)) { IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI); if (node != null) { node.removePreferenceChangeListener(fUIListener); reconcileForPropertyChange(false); node.addPreferenceChangeListener(fUIListener); } } else if (property.equals(AntEditorPreferenceConstants.CODEASSIST_USER_DEFINED_TASKS)) { reconcileForPropertyChange(false); } else if (property.equals(AntEditorPreferenceConstants.BUILDFILE_NAMES_TO_IGNORE) || property.equals(AntEditorPreferenceConstants.BUILDFILE_IGNORE_ALL)) { fReportingProblemsCurrent = false; reconcileForPropertyChange(false); } } }; private Map<String, String> fProperties = null; private List<String> fPropertyFiles = null; private Map<String, String> fDefinersToText; private Map<String, String> fPreviousDefinersToText; private Map<String, List<String>> fDefinerNodeIdentifierToDefinedTasks; private Map<String, AntDefiningTaskNode> fTaskNameToDefiningNode; private Map<String, String> fCurrentNodeIdentifiers; private boolean fReportingProblemsCurrent = false; private boolean fDoNotReportProblems = false; private boolean fShouldReconcile = true; private HashMap<String, String> fNamespacePrefixMappings; public AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) { init(document, problemRequestor, locationProvider); fMarkerUpdater = new AntEditorMarkerUpdater(); fMarkerUpdater.setModel(this); IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntCorePlugin.PI_ANTCORE); if (node != null) { node.addPreferenceChangeListener(fCoreListener); } node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI); if (node != null) { node.addPreferenceChangeListener(fUIListener); } } public AntModel(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider, boolean resolveLexicalInfo, boolean resolvePositionInfo, boolean resolveTaskInfo) { init(document, problemRequestor, locationProvider); fHasLexicalInfo = resolveLexicalInfo; fHasPositionInfo = resolvePositionInfo; fHasTaskInfo = resolveTaskInfo; } private void init(IDocument document, IProblemRequestor problemRequestor, LocationProvider locationProvider) { fDocument = document; fProblemRequestor = problemRequestor; fLocationProvider = locationProvider; if (fgInstanceCount == 0) { // no other models are open to ensure that the classpath is up to date wrt the // Ant preferences and start listening for breakpoint changes AntDefiningTaskNode.setJavaClassPath(); AntModelCore.getDefault().startBreakpointListening(); } fgInstanceCount++; DecayCodeCompletionDataStructuresThread.cancel(); ProjectHelper helper = getProjectHelper(); if (helper == null) { ProjectHelperRepository.getInstance().registerProjectHelper(ProjectHelper.class); } computeEncoding(); } /** * Searches the collection of registered {@link org.apache.tools.ant.ProjectHelper}s to see if we have one registered already. * * @return the {@link ProjectHelper} from our implementation of <code>null</code> if we have not registered one yet * @since 3.7 * @see ProjectHelperRepository */ ProjectHelper getProjectHelper() { Iterator<org.apache.tools.ant.ProjectHelper> helpers = ProjectHelperRepository.getInstance().getHelpers(); while (helpers.hasNext()) { org.apache.tools.ant.ProjectHelper helper = helpers.next(); if (helper instanceof ProjectHelper) { return (ProjectHelper) helper; } } return null; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#dispose() */ @Override public void dispose() { synchronized (getLockObject()) { if (fDocument != null && fListener != null) { fDocument.removeDocumentListener(fListener); } fDocument = null; fLocationProvider = null; ProjectHelper.setAntModel(null); } IEclipsePreferences node = InstanceScope.INSTANCE.getNode(AntCorePlugin.PI_ANTCORE); if (node != null) { node.removePreferenceChangeListener(fCoreListener); } node = InstanceScope.INSTANCE.getNode(AntUIPlugin.PI_ANTUI); if (node != null) { node.removePreferenceChangeListener(fUIListener); } fgInstanceCount--; if (fgInstanceCount == 0) { fgClassLoader = null; DecayCodeCompletionDataStructuresThread.getDefault().start(); AntModelCore.getDefault().stopBreakpointListening(); cleanup(); } } private Object getLockObject() { if (fDocument instanceof ISynchronizable) { Object lock = ((ISynchronizable) fDocument).getLockObject(); if (lock != null) { return lock; } } return this; } private void cleanup() { AntProjectNode projectNode = getProjectNode(); if (projectNode != null) { // cleanup the introspection helpers that may have been generated IntrospectionHelper.clearCache(); projectNode.getProject().fireBuildFinished(null); } fEncoding = null; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#reconcile() */ @Override public void reconcile() { synchronized (fDirtyLock) { if (!fShouldReconcile || !fIsDirty) { return; } fIsDirty = false; } synchronized (getLockObject()) { if (fLocationProvider == null) { // disposed return; } if (fDocument == null) { fProjectNode = null; } else { reset(); parseDocument(fDocument); reconcileTaskAndTypes(); } AntModelCore.getDefault().notifyAntModelListeners(new AntModelChangeEvent(this)); } } private void reset() { fCurrentTargetNode = null; fStillOpenElements = new Stack<>(); fTaskToNode = new HashMap<>(); fTaskNodes = new ArrayList<>(); fNodeBeingResolved = null; fNodeBeingResolvedIndex = -1; fLastNode = null; fCurrentNodeIdentifiers = null; fNamespacePrefixMappings = null; fNonStructuralNodes = new ArrayList<>(1); if (fDefinersToText != null) { fPreviousDefinersToText = new HashMap<>(fDefinersToText); fDefinersToText = null; } } private void parseDocument(IDocument input) { boolean parsed = true; if (input.getLength() == 0) { fProjectNode = null; parsed = false; return; } ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader parsingClassLoader = getClassLoader(originalClassLoader); Thread.currentThread().setContextClassLoader(parsingClassLoader); Project project = null; try { ProjectHelper projectHelper = null; String textToParse = input.get(); if (fProjectNode == null || !fProjectNode.hasChildren()) { fProjectNode = null; project = new AntModelProject(); projectHelper = prepareForFullParse(project, parsingClassLoader); } else { project = fProjectNode.getProject(); projectHelper = (ProjectHelper) project.getReference("ant.projectHelper"); //$NON-NLS-1$ projectHelper.setBuildFile(getEditedFile()); prepareForFullIncremental(); } beginReporting(); Map<String, Object> references = project.getReferences(); references.remove("ant.parsing.context"); //$NON-NLS-1$ ProjectHelper.setAntModel(this); projectHelper.parse(project, textToParse); } catch (BuildException e) { handleBuildException(e, null); } finally { if (parsed) { SecurityManager origSM = System.getSecurityManager(); processAntHome(true); try { // set a security manager to disallow system exit and system property setting System.setSecurityManager(new AntSecurityManager(origSM, Thread.currentThread(), false)); resolveBuildfile(); endReporting(); } catch (AntSecurityException e) { // do nothing } finally { Thread.currentThread().setContextClassLoader(originalClassLoader); getClassLoader(null); System.setSecurityManager(origSM); project.fireBuildFinished(null); // cleanup (IntrospectionHelper) } } } } private ProjectHelper prepareForFullParse(Project project, ClassLoader parsingClassLoader) { initializeProject(project, parsingClassLoader); // Ant's parsing facilities always works on a file, therefore we need // to determine the actual location of the file. Though the file // contents will not be parsed. We parse the passed document string File file = getEditedFile(); String filePath = IAntCoreConstants.EMPTY_STRING; if (file != null) { filePath = file.getAbsolutePath(); } project.setUserProperty("ant.file", filePath); //$NON-NLS-1$ project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$ ProjectHelper projectHelper = getProjectHelper(); ProjectHelper.setAntModel(this); projectHelper.setBuildFile(file); project.addReference("ant.projectHelper", projectHelper); //$NON-NLS-1$ return projectHelper; } private void prepareForFullIncremental() { fProjectNode.reset(); fTaskToNode = new HashMap<>(); fTaskNodes = new ArrayList<>(); } private void initializeProject(Project project, ClassLoader loader) { try { processAntHome(false); } catch (AntSecurityException ex) { // do nothing - Ant home can not be set from this thread } project.init(); setProperties(project); setTasks(project, loader); setTypes(project, loader); } private void setTasks(Project project, ClassLoader loader) { List<org.eclipse.ant.core.Task> tasks = AntCorePlugin.getPlugin().getPreferences().getTasks(); for (org.eclipse.ant.core.Task task : tasks) { AntTypeDefinition def = new AntTypeDefinition(); def.setName(task.getTaskName()); def.setClassName(task.getClassName()); def.setClassLoader(loader); def.setAdaptToClass(Task.class); def.setAdapterClass(TaskAdapter.class); ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def); } } private void setTypes(Project project, ClassLoader loader) { List<Type> types = AntCorePlugin.getPlugin().getPreferences().getTypes(); for (Type type : types) { AntTypeDefinition def = new AntTypeDefinition(); def.setName(type.getTypeName()); def.setClassName(type.getClassName()); def.setClassLoader(loader); ComponentHelper.getComponentHelper(project).addDataTypeDefinition(def); } } private void setProperties(Project project) { setBuiltInProperties(project); setExtraProperties(project); setGlobalProperties(project); loadExtraPropertyFiles(project); loadPropertyFiles(project); } private void setExtraProperties(Project project) { if (fProperties != null) { Pattern pattern = Pattern.compile("\\$\\{.*_prompt.*\\}"); //$NON-NLS-1$ IStringVariableManager manager = VariablesPlugin.getDefault().getStringVariableManager(); for (Iterator<String> iter = fProperties.keySet().iterator(); iter.hasNext();) { String name = iter.next(); String value = fProperties.get(name); if (!pattern.matcher(value).find()) { try { value = manager.performStringSubstitution(value); } catch (CoreException e) { // do nothing } } if (value != null) { project.setUserProperty(name, value); } } } } private void loadExtraPropertyFiles(Project project) { if (fPropertyFiles != null) { try { List<Properties> allProperties = AntCoreUtil.loadPropertyFiles(fPropertyFiles, project.getUserProperty("basedir"), getEditedFile().getAbsolutePath()); //$NON-NLS-1$ setPropertiesFromFiles(project, allProperties); } catch (IOException e1) { AntUIPlugin.log(e1); } } } /** * Load all properties from the files */ private void loadPropertyFiles(Project project) { List<String> fileNames = Arrays.asList(AntCorePlugin.getPlugin().getPreferences().getCustomPropertyFiles()); try { List<Properties> allProperties = AntCoreUtil.loadPropertyFiles(fileNames, project.getUserProperty("basedir"), getEditedFile().getAbsolutePath()); //$NON-NLS-1$ setPropertiesFromFiles(project, allProperties); } catch (IOException e1) { AntUIPlugin.log(e1); } } private void setPropertiesFromFiles(Project project, List<Properties> allProperties) { for (Properties props : allProperties) { Enumeration<?> propertyNames = props.propertyNames(); while (propertyNames.hasMoreElements()) { String name = (String) propertyNames.nextElement(); // do not override extra local properties with the global settings if (project.getUserProperty(name) == null) { project.setUserProperty(name, props.getProperty(name)); } } } } private void setBuiltInProperties(Project project) { // set processAntHome for other properties set as system properties project.setUserProperty("ant.file", getEditedFile().getAbsolutePath()); //$NON-NLS-1$ project.setUserProperty("ant.version", Main.getAntVersion()); //$NON-NLS-1$ } private void processAntHome(boolean finished) { AntCorePreferences prefs = AntCorePlugin.getPlugin().getPreferences(); String antHome = prefs.getAntHome(); if (finished || antHome == null) { System.getProperties().remove("ant.home"); //$NON-NLS-1$ System.getProperties().remove("ant.library.dir"); //$NON-NLS-1$ } else { System.setProperty("ant.home", antHome); //$NON-NLS-1$ File antLibDir = new File(antHome, "lib"); //$NON-NLS-1$ System.setProperty("ant.library.dir", antLibDir.getAbsolutePath()); //$NON-NLS-1$ } } private void setGlobalProperties(Project project) { List<Property> properties = AntCorePlugin.getPlugin().getPreferences().getProperties(); if (properties != null) { for (Iterator<Property> iter = properties.iterator(); iter.hasNext();) { Property property = iter.next(); String value = property.getValue(true); if (value != null) { project.setUserProperty(property.getName(), value); } } } } private void resolveBuildfile() { Collection<AntTaskNode> nodeCopy = new ArrayList<>(fTaskNodes); Iterator<AntTaskNode> iter = nodeCopy.iterator(); while (iter.hasNext()) { AntTaskNode node = iter.next(); fNodeBeingResolved = node; fNodeBeingResolvedIndex = -1; if (node.configure(false)) { // resolve any new elements that may have been added resolveBuildfile(); } } fNodeBeingResolved = null; fNodeBeingResolvedIndex = -1; checkTargets(); } /** * Check that if a default target is specified it exists and that the target dependencies exist. */ private void checkTargets() { if (fProjectNode == null || doNotReportProblems()) { return; } String defaultTargetName = fProjectNode.getDefaultTargetName(); if (defaultTargetName != null && fProjectNode.getProject().getTargets().get(defaultTargetName) == null) { // no default target when one specified (default target does not have to be specified) String message = MessageFormat.format(AntModelMessages.AntModel_43, new Object[] { defaultTargetName }); IProblem problem = createProblem(message, fProjectNode.getOffset(), fProjectNode.getSelectionLength(), AntModelProblem.SEVERITY_ERROR); acceptProblem(problem); markHierarchy(fProjectNode, AntModelProblem.SEVERITY_ERROR, message); } if (!fProjectNode.hasChildren()) { return; } List<IAntElement> children = fProjectNode.getChildNodes(); boolean checkCircularDependencies = true; for (IAntElement node : children) { IAntElement originalNode = node; if (node instanceof AntTargetNode) { if (checkCircularDependencies) { checkCircularDependencies = false; checkCircularDependencies(node); } checkMissingDependencies(node, originalNode); } } } /** * method assumes sender has checked whether to report problems */ private void checkMissingDependencies(IAntElement node, IAntElement originalNode) { String missing = ((AntTargetNode) node).checkDependencies(); if (missing != null) { String message = MessageFormat.format(AntModelMessages.AntModel_44, new Object[] { missing }); IAntElement importNode = node.getImportNode(); if (importNode != null) { node = importNode; } IProblem problem = createProblem(message, node.getOffset(), node.getSelectionLength(), AntModelProblem.SEVERITY_ERROR); acceptProblem(problem); markHierarchy(originalNode, AntModelProblem.SEVERITY_ERROR, message); } } private boolean doNotReportProblems() { if (fReportingProblemsCurrent) { return fDoNotReportProblems; } fReportingProblemsCurrent = true; fDoNotReportProblems = false; if (AntUIPlugin.getDefault().getCombinedPreferenceStore().getBoolean(AntEditorPreferenceConstants.BUILDFILE_IGNORE_ALL)) { fDoNotReportProblems = true; return fDoNotReportProblems; } String buildFileNames = AntUIPlugin.getDefault().getCombinedPreferenceStore().getString(AntEditorPreferenceConstants.BUILDFILE_NAMES_TO_IGNORE); if (buildFileNames.length() > 0) { String[] names = AntUtil.parseString(buildFileNames, ","); //$NON-NLS-1$ String editedFileName = getEditedFile().getName(); for (int i = 0; i < names.length; i++) { String string = names[i]; if (string.trim().equals(editedFileName)) { fDoNotReportProblems = true; return fDoNotReportProblems; } } } return fDoNotReportProblems; } /** * method assumes sendor has checked whether to report problems */ private void checkCircularDependencies(IAntElement node) { Target target = ((AntTargetNode) node).getTarget(); String name = target.getName(); if (name == null) { return; } try { target.getProject().topoSort(name, target.getProject().getTargets()); } catch (BuildException be) { // possible circular dependency String message = be.getMessage(); if (message.startsWith("Circular")) { //$NON-NLS-1$ //we do our own checking for missing dependencies IProblem problem = createProblem(message, node.getProjectNode().getOffset(), node.getProjectNode().getSelectionLength(), AntModelProblem.SEVERITY_ERROR); acceptProblem(problem); markHierarchy(node.getProjectNode(), AntModelProblem.SEVERITY_ERROR, message); } } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#handleBuildException(org.apache.tools.ant.BuildException, * org.eclipse.ant.internal.ui.model.AntElementNode, int) */ @Override public void handleBuildException(BuildException e, AntElementNode node, int severity) { try { if (node != null) { markHierarchy(node, severity, e.getMessage()); } Location location = e.getLocation(); int line = 0; int originalOffset = 0; int nonWhitespaceOffset = 0; int length = 0; if (location == Location.UNKNOWN_LOCATION && node != null) { if (node.getImportNode() != null) { node = node.getImportNode(); } nonWhitespaceOffset = node.getOffset(); length = node.getLength(); } else { line = location.getLineNumber(); if (line == 0) { AntProjectNode projectNode = getProjectNode(); if (projectNode != null) { length = projectNode.getSelectionLength(); nonWhitespaceOffset = projectNode.getOffset(); if (severity == AntModelProblem.SEVERITY_ERROR) { projectNode.setProblemSeverity(AntModelProblem.NO_PROBLEM); projectNode.setProblemMessage(null); } } else { return; } } else { if (node == null) { originalOffset = getOffset(line, 1); nonWhitespaceOffset = originalOffset; try { nonWhitespaceOffset = getNonWhitespaceOffset(line, 1); } catch (BadLocationException be) { // do nothing } length = getLastCharColumn(line) - (nonWhitespaceOffset - originalOffset); } else { if (node.getImportNode() != null) { node = node.getImportNode(); } nonWhitespaceOffset = node.getOffset(); length = node.getLength(); } } } notifyProblemRequestor(e, nonWhitespaceOffset, length, severity); } catch (BadLocationException e1) { // do nothing } } public void handleBuildException(BuildException e, AntElementNode node) { handleBuildException(e, node, AntModelProblem.SEVERITY_ERROR); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getEditedFile() */ @Override public File getEditedFile() { if (fLocationProvider != null && fEditedFile == null) { fEditedFile = fLocationProvider.getLocation().toFile(); } return fEditedFile; } private void markHierarchy(IAntElement openElement, int severity, String message) { if (doNotReportProblems()) { return; } while (openElement != null) { openElement.setProblemSeverity(severity); openElement.setProblemMessage(message); openElement = openElement.getParentNode(); } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getLocationProvider() */ @Override public LocationProvider getLocationProvider() { return fLocationProvider; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#addTarget(org.apache.tools.ant.Target, int, int) */ @Override public void addTarget(Target newTarget, int line, int column) { AntTargetNode targetNode = AntTargetNode.newAntTargetNode(newTarget); fProjectNode.addChildNode(targetNode); fCurrentTargetNode = targetNode; fStillOpenElements.push(targetNode); if (fNodeBeingResolved instanceof AntImportNode) { targetNode.setImportNode(fNodeBeingResolved); targetNode.setExternal(true); targetNode.setFilePath(newTarget.getLocation().getFileName()); } else { String targetFileName = newTarget.getLocation().getFileName(); boolean external = isNodeExternal(targetFileName); targetNode.setExternal(external); if (external) { targetNode.setFilePath(targetFileName); } } computeOffset(targetNode, line, column); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#addProject(org.apache.tools.ant.Project, int, int) */ @Override public void addProject(Project project, int line, int column) { fProjectNode = new AntProjectNode((AntModelProject) project, this); fStillOpenElements.push(fProjectNode); computeOffset(fProjectNode, line, column); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#addDTD(java.lang.String, int, int) */ @Override public void addDTD(String name, int line, int column) { AntDTDNode node = new AntDTDNode(name); fStillOpenElements.push(node); int offset = -1; try { if (column <= 0) { offset = getOffset(line, 0); int lastCharColumn = getLastCharColumn(line); offset = computeOffsetUsingPrefix(line, offset, "<!DOCTYPE", lastCharColumn); //$NON-NLS-1$ } else { offset = getOffset(line, column); } } catch (BadLocationException e) { AntUIPlugin.log(e); } node.setOffset(offset); fNonStructuralNodes.add(node); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#addTask(org.apache.tools.ant.Task, org.apache.tools.ant.Task, org.xml.sax.Attributes, int, * int) */ @Override public void addTask(Task newTask, Task parentTask, Attributes attributes, int line, int column) { if (!canGetTaskInfo()) { // need to add top level tasks so imports are executed even when // the model is not interested in task level resolution Target owningTarget = newTask.getOwningTarget(); String name = owningTarget.getName(); if (name == null || name.length() != 0) { // not the top level implicit target return; } } AntTaskNode taskNode = null; if (parentTask == null) { taskNode = newTaskNode(newTask, attributes); if (fCurrentTargetNode == null) { fProjectNode.addChildNode(taskNode); } else { fCurrentTargetNode.addChildNode(taskNode); } } else { taskNode = newNotWellKnownTaskNode(newTask, attributes); AntTaskNode parentNode = fTaskToNode.get(parentTask); parentNode.addChildNode(taskNode); } fTaskToNode.put(newTask, taskNode); fStillOpenElements.push(taskNode); computeOffset(taskNode, line, column); if (fNodeBeingResolved instanceof AntImportNode) { taskNode.setImportNode(fNodeBeingResolved); // place the node in the collection right after the import node if (fNodeBeingResolvedIndex == -1) { fNodeBeingResolvedIndex = fTaskNodes.indexOf(fNodeBeingResolved); } fNodeBeingResolvedIndex++; fTaskNodes.add(fNodeBeingResolvedIndex, taskNode); } else { fTaskNodes.add(taskNode); } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#addEntity(java.lang.String, java.lang.String) */ @Override public void addEntity(String entityName, String entityPath) { if (fEntityNameToPath == null) { fEntityNameToPath = new HashMap<>(); } fEntityNameToPath.put(entityName, entityPath); } private AntTaskNode newTaskNode(Task newTask, Attributes attributes) { AntTaskNode newNode = null; String taskName = newTask.getTaskName(); if (newTask instanceof UnknownElement) { // attempt to handle namespaces taskName = ((UnknownElement) newTask).getTag(); } if (isPropertySettingTask(taskName)) { newNode = new AntPropertyNode(newTask, attributes); } else if (taskName.equalsIgnoreCase("import")) { //$NON-NLS-1$ newNode = new AntImportNode(newTask, attributes); } else if (taskName.equalsIgnoreCase("macrodef") //$NON-NLS-1$ || taskName.equalsIgnoreCase("presetdef") //$NON-NLS-1$ || taskName.equalsIgnoreCase("typedef") //$NON-NLS-1$ || taskName.equalsIgnoreCase("taskdef")) { //$NON-NLS-1$ newNode = new AntDefiningTaskNode(newTask, attributes); } else if (taskName.equalsIgnoreCase("antcall")) { //$NON-NLS-1$ newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntModelConstants.ATTR_TARGET)); } else if (taskName.equalsIgnoreCase("mkdir")) { //$NON-NLS-1$ newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntCoreConstants.DIR)); } else if (taskName.equalsIgnoreCase("copy")) { //$NON-NLS-1$ newNode = new AntTaskNode(newTask, generateLabel(taskName, attributes, IAntModelConstants.ATTR_DESTFILE)); } else if (taskName.equalsIgnoreCase("tar") //$NON-NLS-1$ || taskName.equalsIgnoreCase("jar") //$NON-NLS-1$ || taskName.equalsIgnoreCase("war") //$NON-NLS-1$ || taskName.equalsIgnoreCase("zip")) { //$NON-NLS-1$ newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_DESTFILE)); } else if (taskName.equalsIgnoreCase("untar") //$NON-NLS-1$ || taskName.equalsIgnoreCase("unjar") //$NON-NLS-1$ || taskName.equalsIgnoreCase("unwar") //$NON-NLS-1$ || taskName.equalsIgnoreCase("gunzip") //$NON-NLS-1$ || taskName.equalsIgnoreCase("bunzip2") //$NON-NLS-1$ || taskName.equalsIgnoreCase("unzip")) { //$NON-NLS-1$ newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_SRC)); } else if (taskName.equalsIgnoreCase("gzip") //$NON-NLS-1$ || taskName.equalsIgnoreCase("bzip2")) { //$NON-NLS-1$ newNode = new AntTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntModelConstants.ATTR_ZIPFILE)); } else if (taskName.equalsIgnoreCase("exec")) { //$NON-NLS-1$ String label = "exec "; //$NON-NLS-1$ String command = attributes.getValue(IAntModelConstants.ATTR_COMMAND); if (command != null) { label += command; } command = attributes.getValue(IAntModelConstants.ATTR_EXECUTABLE); if (command != null) { label += command; } newNode = new AntTaskNode(newTask, label); } else if (taskName.equalsIgnoreCase("ant")) { //$NON-NLS-1$ newNode = new AntAntNode(newTask, attributes); } else if (taskName.equalsIgnoreCase("delete")) { //$NON-NLS-1$ String label = "delete "; //$NON-NLS-1$ String file = attributes.getValue(IAntCoreConstants.FILE); if (file != null) { label += file; } else { file = attributes.getValue(IAntCoreConstants.DIR); if (file != null) { label += file; } } newNode = new AntTaskNode(newTask, label); } else if (IAntCoreConstants.AUGMENT.equals(taskName)) { newNode = new AntAugmentTaskNode(newTask, generateLabel(newTask.getTaskName(), attributes, IAntCoreConstants.ID)); } else { newNode = newNotWellKnownTaskNode(newTask, attributes); } setExternalInformation(newTask, newNode); return newNode; } /** * @param taskName * the name of the task to check * @return whether or not a task with this name sets properties */ private boolean isPropertySettingTask(String taskName) { return taskName.equalsIgnoreCase("property") //$NON-NLS-1$ || taskName.equalsIgnoreCase("available") //$NON-NLS-1$ || taskName.equalsIgnoreCase("basename") //$NON-NLS-1$ || taskName.equalsIgnoreCase("condition") //$NON-NLS-1$ || taskName.equalsIgnoreCase("dirname") //$NON-NLS-1$ || taskName.equalsIgnoreCase("loadfile") //$NON-NLS-1$ || taskName.equalsIgnoreCase("pathconvert") //$NON-NLS-1$ || taskName.equalsIgnoreCase("uptodate") //$NON-NLS-1$ || taskName.equalsIgnoreCase("xmlproperty") //$NON-NLS-1$ || taskName.equalsIgnoreCase("loadproperties"); //$NON-NLS-1$ } private boolean isNodeExternal(String fileName) { File taskFile = new File(fileName); return !taskFile.equals(getEditedFile()); } private AntTaskNode newNotWellKnownTaskNode(Task newTask, Attributes attributes) { AntTaskNode newNode = new AntTaskNode(newTask); String id = attributes.getValue("id"); //$NON-NLS-1$ if (id != null) { newNode.setId(id); } String taskName = newTask.getTaskName(); if ("attribute".equals(taskName) || "element".equals(taskName)) { //$NON-NLS-1$ //$NON-NLS-2$ String name = attributes.getValue(IAntCoreConstants.NAME); if (name != null) { newNode.setBaseLabel(name); } } setExternalInformation(newTask, newNode); return newNode; } private void setExternalInformation(Task newTask, AntTaskNode newNode) { String taskFileName = newTask.getLocation().getFileName(); boolean external = isNodeExternal(taskFileName); newNode.setExternal(external); if (external) { newNode.setFilePath(taskFileName); } } private String generateLabel(String taskName, Attributes attributes, String attributeName) { StringBuffer label = new StringBuffer(taskName); String srcFile = attributes.getValue(attributeName); if (srcFile != null) { label.append(' '); label.append(srcFile); } return label.toString(); } private void computeLength(AntElementNode element, int line, int column) { if (element.isExternal()) { element.setExternalInfo(line, column); return; } try { int length; int offset; if (column <= 0) { column = getLastCharColumn(line); String lineText = fDocument.get(fDocument.getLineOffset(line - 1), column); StringBuffer searchString = new StringBuffer("</"); //$NON-NLS-1$ searchString.append(element.getName()); searchString.append('>'); int index = lineText.indexOf(searchString.toString()); if (index == -1) { index = lineText.indexOf("/>"); //$NON-NLS-1$ if (index == -1) { index = column; // set to the end of line } else { index = index + 3; } } else { index = index + searchString.length() + 1; } offset = getOffset(line, index); } else { offset = getOffset(line, column); } length = offset - element.getOffset(); element.setLength(length); } catch (BadLocationException e) { // ignore as the parser may be out of sync with the document during reconciliation } } private void computeOffset(AntElementNode element, int line, int column) { if (!canGetPositionInfo()) { return; } if (element.isExternal()) { element.setExternalInfo(line - 1, column); return; } try { String prefix = "<" + element.getName(); //$NON-NLS-1$ int offset = computeOffset(line, column, prefix); element.setOffset(offset + 1); element.setSelectionLength(element.getName().length()); } catch (BadLocationException e) { // ignore as the parser may be out of sync with the document during reconciliation } } private int computeOffset(int line, int column, String prefix) throws BadLocationException { int offset; if (column <= 0) { offset = getOffset(line, 0); int lastCharColumn = getLastCharColumn(line); offset = computeOffsetUsingPrefix(line, offset, prefix, lastCharColumn); } else { column = column - 1; offset = getOffset(line, column); offset = computeOffsetUsingPrefix(line, offset, prefix, column); } return offset; } private int computeOffsetUsingPrefix(int line, int offset, String prefix, int column) throws BadLocationException { String lineText = fDocument.get(fDocument.getLineOffset(line - 1), column); int lastIndex = lineText.indexOf(prefix); if (lastIndex > -1) { offset = getOffset(line, lastIndex + 1); } else { return computeOffsetUsingPrefix(line - 1, offset, prefix, getLastCharColumn(line - 1)); } return offset; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getOffset(int, int) */ @Override public int getOffset(int line, int column) throws BadLocationException { return fDocument.getLineOffset(line - 1) + column - 1; } private int getNonWhitespaceOffset(int line, int column) throws BadLocationException { int offset = fDocument.getLineOffset(line - 1) + column - 1; while (Character.isWhitespace(fDocument.getChar(offset))) { offset++; } return offset; } public int getLine(int offset) { try { return fDocument.getLineOfOffset(offset) + 1; } catch (BadLocationException be) { return -1; } } private int getLastCharColumn(int line) throws BadLocationException { String lineDelimiter = fDocument.getLineDelimiter(line - 1); int lineDelimiterLength = lineDelimiter != null ? lineDelimiter.length() : 0; return fDocument.getLineLength(line - 1) - lineDelimiterLength; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#setCurrentElementLength(int, int) */ @Override public void setCurrentElementLength(int lineNumber, int column) { fLastNode = fStillOpenElements.pop(); if (fLastNode == fCurrentTargetNode) { fCurrentTargetNode = null; // the current target element has been closed } if (canGetPositionInfo()) { computeLength(fLastNode, lineNumber, column); } } private void acceptProblem(IProblem problem) { if (fProblemRequestor != null) { fProblemRequestor.acceptProblem(problem); } if (fMarkerUpdater != null) { fMarkerUpdater.acceptProblem(problem); } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getFile() */ @Override public IFile getFile() { IPath location = fLocationProvider.getLocation(); if (location == null) { return null; } IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(location.toFile().toURI()); if (files.length > 0) { return files[0]; } return null; } private void beginReporting() { if (fProblemRequestor != null) { fProblemRequestor.beginReporting(); } if (fMarkerUpdater != null) { fMarkerUpdater.beginReporting(); } } private void endReporting() { if (fProblemRequestor != null) { fProblemRequestor.endReporting(); } } private IProblem createProblem(Exception exception, int offset, int length, int severity) { return createProblem(exception.getMessage(), offset, length, severity); } private IProblem createProblem(String message, int offset, int length, int severity) { return new AntModelProblem(message, severity, offset, length, getLine(offset)); } private void notifyProblemRequestor(Exception exception, IAntElement element, int severity) { if (doNotReportProblems()) { return; } IAntElement importNode = element.getImportNode(); if (importNode != null) { element = importNode; } IProblem problem = createProblem(exception, element.getOffset(), element.getLength(), severity); acceptProblem(problem); element.setProblem(problem); } private void notifyProblemRequestor(Exception exception, int offset, int length, int severity) { if (doNotReportProblems()) { return; } if (fProblemRequestor != null) { IProblem problem = createProblem(exception, offset, length, severity); acceptProblem(problem); } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#warning(java.lang.Exception) */ @Override public void warning(Exception exception) { handleError(exception, AntModelProblem.SEVERITY_WARNING); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#error(java.lang.Exception) */ @Override public void error(Exception exception) { handleError(exception, AntModelProblem.SEVERITY_ERROR); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#errorFromElementText(java.lang.Exception, int, int) */ @Override public void errorFromElementText(Exception exception, int start, int count) { AntElementNode node = fLastNode; if (node == null) { if (!fStillOpenElements.empty()) { node = fStillOpenElements.peek(); } } if (node == null) { return; } computeEndLocationForErrorNode(node, start, count); notifyProblemRequestor(exception, start, count, AntModelProblem.SEVERITY_ERROR); markHierarchy(fLastNode, AntModelProblem.SEVERITY_ERROR, exception.getMessage()); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#errorFromElement(java.lang.Exception, org.eclipse.ant.internal.ui.model.AntElementNode, int, * int) */ @Override public void errorFromElement(Exception exception, AntElementNode node, int lineNumber, int column) { if (node == null) { if (!fStillOpenElements.empty()) { node = fStillOpenElements.peek(); } else { node = fLastNode; } } computeEndLocationForErrorNode(node, lineNumber, column); notifyProblemRequestor(exception, node, AntModelProblem.SEVERITY_ERROR); markHierarchy(node, AntModelProblem.SEVERITY_ERROR, exception.getMessage()); } private AntElementNode createProblemElement(SAXParseException exception) { int lineNumber = exception.getLineNumber(); StringBuffer message = new StringBuffer(exception.getMessage()); if (lineNumber != -1) { message.append(AntModelMessages.AntModel_1 + lineNumber); } AntElementNode errorNode = new AntElementNode(message.toString()); errorNode.setFilePath(exception.getSystemId()); errorNode.setProblemSeverity(AntModelProblem.SEVERITY_ERROR); errorNode.setProblemMessage(exception.getMessage()); computeErrorLocation(errorNode, exception); return errorNode; } private void computeErrorLocation(AntElementNode element, SAXParseException exception) { if (element.isExternal()) { return; } int line = exception.getLineNumber(); int startColumn = exception.getColumnNumber(); computeEndLocationForErrorNode(element, line, startColumn); } private void computeEndLocationForErrorNode(IAntElement element, int line, int startColumn) { try { if (line <= 0) { line = 1; } int endColumn; if (startColumn <= 0) { if (element.getOffset() > -1) { startColumn = element.getOffset() + 1; } else { startColumn = 1; } endColumn = getLastCharColumn(line) + 1; } else { if (startColumn > 1) { --startColumn; } endColumn = startColumn; if (startColumn <= getLastCharColumn(line)) { ++endColumn; } } int correction = 0; if (element.getOffset() == -1) { int originalOffset = getOffset(line, startColumn); int nonWhitespaceOffset = originalOffset; try { nonWhitespaceOffset = getNonWhitespaceOffset(line, startColumn); } catch (BadLocationException be) { // do nothing } element.setOffset(nonWhitespaceOffset); correction = nonWhitespaceOffset - originalOffset; } if (endColumn - startColumn == 0) { int offset = getOffset(line, startColumn); element.setLength(offset - element.getOffset() - correction); } else { element.setLength(endColumn - startColumn - correction); } } catch (BadLocationException e) { // ignore as the parser may be out of sync with the document during reconciliation } } private void handleError(Exception exception, int severity) { IAntElement node = null; if (fStillOpenElements.isEmpty()) { if (exception instanceof SAXParseException) { node = createProblemElement((SAXParseException) exception); } } else { node = fStillOpenElements.peek(); } if (node == null) { return; } markHierarchy(node, severity, exception.getMessage()); if (exception instanceof SAXParseException) { SAXParseException parseException = (SAXParseException) exception; if (node.getOffset() == -1) { computeEndLocationForErrorNode(node, parseException.getLineNumber() - 1, parseException.getColumnNumber()); } else { int lineNumber = parseException.getLineNumber(); int columnNumber = parseException.getColumnNumber(); if (columnNumber == -1) { columnNumber = 1; } try { AntElementNode childNode = node.getNode(getNonWhitespaceOffset(lineNumber, columnNumber) + 1); if (childNode != null && childNode != node) { node = childNode; node.setProblemSeverity(severity); node.setProblemMessage(exception.getMessage()); } else { node = createProblemElement(parseException); } } catch (BadLocationException be) { node = createProblemElement(parseException); } } } notifyProblemRequestor(exception, node, severity); if (node != null) { while (node.getParentNode() != null) { IAntElement parentNode = node.getParentNode(); if (parentNode.getLength() == -1) { parentNode.setLength(node.getOffset() - parentNode.getOffset() + node.getLength()); } node = parentNode; } } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#fatalError(java.lang.Exception) */ @Override public void fatalError(Exception exception) { handleError(exception, AntModelProblem.SEVERITY_FATAL_ERROR); } public AntElementNode getOpenElement() { if (fStillOpenElements.isEmpty()) { return null; } return fStillOpenElements.peek(); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getEntityName(java.lang.String) */ @Override public String getEntityName(String path) { if (fEntityNameToPath != null) { Iterator<String> itr = fEntityNameToPath.keySet().iterator(); String entityPath; String name; while (itr.hasNext()) { name = itr.next(); entityPath = fEntityNameToPath.get(name); if (entityPath.equals(path)) { return name; } } } return null; } private ClassLoader getClassLoader(ClassLoader contextClassLoader) { synchronized (loaderLock) { if (fLocalClassLoader != null) { ((AntClassLoader) fLocalClassLoader).setPluginContextClassloader(contextClassLoader); return fLocalClassLoader; } if (fgClassLoader == null) { fgClassLoader = AntCorePlugin.getPlugin().getNewClassLoader(true); } if (fgClassLoader instanceof AntClassLoader) { ((AntClassLoader) fgClassLoader).setPluginContextClassloader(contextClassLoader); } return fgClassLoader; } } public String getTargetDescription(String targetName) { AntTargetNode target = getTargetNode(targetName); if (target != null) { return target.getTarget().getDescription(); } return null; } public AntTargetNode getTargetNode(String targetName) { AntProjectNode projectNode = getProjectNode(); if (projectNode == null) { return null; } if (projectNode.hasChildren()) { List<IAntElement> possibleTargets = projectNode.getChildNodes(); for (IAntElement node : possibleTargets) { if (node instanceof AntTargetNode) { AntTargetNode targetNode = (AntTargetNode) node; if (targetName.equalsIgnoreCase(targetNode.getTarget().getName())) { return targetNode; } } } } return null; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getProjectNode(boolean) */ @Override public AntProjectNode getProjectNode(boolean doReconcile) { if (doReconcile) { synchronized (getLockObject()) { // ensure to wait for any current synchronization reconcile(); } } return fProjectNode; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getProjectNode() */ @Override public AntProjectNode getProjectNode() { return getProjectNode(true); } public AntElementNode getNode(int offset, boolean waitForReconcile) { if (getProjectNode(waitForReconcile) != null) { return getProjectNode(waitForReconcile).getNode(offset); } return null; } /** * Removes any type definitions that no longer exist in the buildfile */ private void reconcileTaskAndTypes() { if (fCurrentNodeIdentifiers == null || fDefinerNodeIdentifierToDefinedTasks == null) { return; } Iterator<String> iter = fDefinerNodeIdentifierToDefinedTasks.keySet().iterator(); ComponentHelper helper = ComponentHelper.getComponentHelper(fProjectNode.getProject()); while (iter.hasNext()) { String key = iter.next(); if (fCurrentNodeIdentifiers.get(key) == null) { removeDefinerTasks(key, helper.getAntTypeTable()); } } } protected void removeDefinerTasks(String definerIdentifier, Hashtable<String, AntTypeDefinition> typeTable) { if (fDefinerNodeIdentifierToDefinedTasks == null) { return; } List<String> tasks = fDefinerNodeIdentifierToDefinedTasks.get(definerIdentifier); if (tasks == null) { return; } Iterator<String> iterator = tasks.iterator(); while (iterator.hasNext()) { typeTable.remove(iterator.next()); } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#addComment(int, int, int) */ @Override public void addComment(int lineNumber, int columnNumber, int length) { AntCommentNode commentNode = new AntCommentNode(); int offset = -1; try { offset = computeOffset(lineNumber, columnNumber, "-->"); //$NON-NLS-1$ } catch (BadLocationException e) { commentNode.setExternal(true); commentNode.setExternalInfo(lineNumber, columnNumber); offset = length - 1; } commentNode.setOffset(offset - length); commentNode.setLength(length); fNonStructuralNodes.add(commentNode); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#needsTaskResolution() */ @Override public boolean canGetTaskInfo() { return fHasTaskInfo; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#needsLexicalResolution() */ @Override public boolean canGetLexicalInfo() { return fHasLexicalInfo; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#setClassLoader(java.net.URLClassLoader) */ @Override public void setClassLoader(URLClassLoader loader) { AntDefiningTaskNode.setJavaClassPath(loader.getURLs()); fLocalClassLoader = loader; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#needsPositionResolution() */ @Override public boolean canGetPositionInfo() { return fHasPositionInfo; } public String getPath(String text, int offset) { if (fEntityNameToPath != null) { String path = fEntityNameToPath.get(text); if (path != null) { return path; } } AntElementNode node = getNode(offset, true); if (node != null) { return node.getReferencedElement(offset); } return null; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getText(int, int) */ @Override public String getText(int offset, int length) { try { return fDocument.get(offset, length); } catch (BadLocationException e) { // do nothing } return null; } private IAntElement findPropertyNode(String text, List<IAntElement> children) { for (IAntElement element : children) { if (element instanceof AntPropertyNode) { if (((AntPropertyNode) element).getProperty(text) != null) { return element; } } else if (element.hasChildren()) { IAntElement found = findPropertyNode(text, element.getChildNodes()); if (found != null) { return found; } } } return null; } public IAntElement getPropertyNode(String text) { AntProjectNode node = getProjectNode(); if (node == null || !node.hasChildren()) { return null; } return findPropertyNode(text, node.getChildNodes()); } public List<AntElementNode> getNonStructuralNodes() { return fNonStructuralNodes; } public void updateForInitialReconcile() { fMarkerUpdater.updateMarkers(); fShouldReconcile = AntUIPlugin.getDefault().getPreferenceStore().getBoolean(AntEditorPreferenceConstants.EDITOR_RECONCILE); } public void updateMarkers() { boolean temp = fShouldReconcile; try { fShouldReconcile = true; reconcile(); fMarkerUpdater.updateMarkers(); } finally { fShouldReconcile = temp; } } public AntElementNode getReferenceNode(String text) { Object reference = getReferenceObject(text); if (reference == null) { return null; } Set<Task> nodes = fTaskToNode.keySet(); Iterator<Task> iter = nodes.iterator(); while (iter.hasNext()) { Task task = iter.next(); Task tmptask = task; if (tmptask instanceof UnknownElement) { UnknownElement element = (UnknownElement) tmptask; RuntimeConfigurable wrapper = element.getWrapper(); Map<String, Object> attributes = wrapper.getAttributeMap(); String id = (String) attributes.get("id"); //$NON-NLS-1$ if (text.equals(id)) { return fTaskToNode.get(task); } } } return null; } public Object getReferenceObject(String refId) { AntProjectNode projectNode = getProjectNode(); if (projectNode == null) { return null; } try { Project project = projectNode.getProject(); Object ref = project.getReference(refId); return ref; } catch (BuildException be) { handleBuildException(be, null); } return null; } public String getPropertyValue(String propertyName) { AntProjectNode projectNode = getProjectNode(); if (projectNode == null) { return null; } return projectNode.getProject().getProperty(propertyName); } /** * Only called if the AntModel is associated with an AntEditor */ public void install() { fListener = new IDocumentListener() { @Override public void documentAboutToBeChanged(DocumentEvent event) { synchronized (fDirtyLock) { fIsDirty = true; } } @Override public void documentChanged(DocumentEvent event) { // do nothing } }; fDocument.addDocumentListener(fListener); } private void reconcileForPropertyChange(boolean classpathChanged) { if (classpathChanged) { fProjectNode = null; // need to reset tasks, types and properties fgClassLoader = null; AntDefiningTaskNode.setJavaClassPath(); ProjectHelper.reset(); } fIsDirty = true; reconcile(); AntModelCore.getDefault().notifyAntModelListeners(new AntModelChangeEvent(this, true)); fMarkerUpdater.updateMarkers(); } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#setProperties(java.util.Map) */ @Override public void setProperties(Map<String, String> properties) { fProperties = properties; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#setPropertyFiles(java.util.List) */ @Override public void setPropertyFiles(String[] propertyFiles) { if (propertyFiles != null) { fPropertyFiles = Arrays.asList(propertyFiles); } } @Override public void setDefiningTaskNodeText(AntDefiningTaskNode node) { if (fDefinersToText == null) { fDefinersToText = new HashMap<>(); fCurrentNodeIdentifiers = new HashMap<>(); } String nodeIdentifier = node.getIdentifier(); String nodeText = null; if (fPreviousDefinersToText != null) { nodeText = fPreviousDefinersToText.get(nodeIdentifier); } String newNodeText = getText(node.getOffset(), node.getLength()); if (nodeText != null) { if (nodeText.equals(newNodeText)) { node.setNeedsToBeConfigured(false); // update the data structures for the new node as the offset may have changed. List<String> tasks = fDefinerNodeIdentifierToDefinedTasks.get(nodeIdentifier); if (tasks != null) { for (Iterator<String> iter = tasks.iterator(); iter.hasNext();) { String taskName = iter.next(); fTaskNameToDefiningNode.put(taskName, node); } } } } if (newNodeText != null) { fDefinersToText.put(nodeIdentifier, newNodeText); } fCurrentNodeIdentifiers.put(nodeIdentifier, nodeIdentifier); } protected void removeDefiningTaskNodeInfo(AntDefiningTaskNode node) { Object identifier = node.getIdentifier(); if (identifier != null && fCurrentNodeIdentifiers != null) { fCurrentNodeIdentifiers.remove(identifier); fDefinersToText.remove(identifier); } } protected void addDefinedTasks(List<String> newTasks, AntDefiningTaskNode node) { if (fTaskNameToDefiningNode == null) { fTaskNameToDefiningNode = new HashMap<>(); fDefinerNodeIdentifierToDefinedTasks = new HashMap<>(); } String identifier = node.getIdentifier(); if (identifier == null) { return; } if (newTasks.isEmpty() && fCurrentNodeIdentifiers != null) { fCurrentNodeIdentifiers.remove(identifier); } fDefinerNodeIdentifierToDefinedTasks.put(identifier, newTasks); Iterator<String> iter = newTasks.iterator(); while (iter.hasNext()) { String name = iter.next(); fTaskNameToDefiningNode.put(name, node); } } public AntDefiningTaskNode getDefininingTaskNode(String nodeName) { if (fTaskNameToDefiningNode != null) { AntDefiningTaskNode node = fTaskNameToDefiningNode.get(nodeName); if (node == null) { nodeName = getNamespaceCorrectName(nodeName); node = fTaskNameToDefiningNode.get(nodeName); } return node; } return null; } public String getNamespaceCorrectName(String nodeName) { String prefix = org.apache.tools.ant.ProjectHelper.extractUriFromComponentName(nodeName); String uri = getPrefixMapping(prefix); nodeName = org.apache.tools.ant.ProjectHelper.genComponentName(uri, org.apache.tools.ant.ProjectHelper.extractNameFromComponentName(nodeName)); return nodeName; } public String getUserNamespaceCorrectName(String nodeName) { String prefix = org.apache.tools.ant.ProjectHelper.extractUriFromComponentName(nodeName); if (prefix.length() > 0) { String uri = getUserPrefixMapping(prefix); nodeName = org.apache.tools.ant.ProjectHelper.genComponentName(uri, org.apache.tools.ant.ProjectHelper.extractNameFromComponentName(nodeName)); } return nodeName; } public AntTaskNode getMacroDefAttributeNode(String macroDefAttributeName) { if (fTaskNameToDefiningNode == null) { return null; } for (AntDefiningTaskNode definingNode : fTaskNameToDefiningNode.values()) { List<IAntElement> attributes = definingNode.getChildNodes(); if (attributes != null) { for (IAntElement element : attributes) { if (macroDefAttributeName.equals(element.getLabel())) { return (AntTaskNode) element; } } } } return null; } /** * Sets whether the AntModel should reconcile if it become dirty. If set to reconcile, a reconcile is triggered if the model is dirty. * * @param shouldReconcile */ public void setShouldReconcile(boolean shouldReconcile) { fShouldReconcile = shouldReconcile; if (fShouldReconcile) { reconcile(); } } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#addPrefixMapping(java.lang.String, java.lang.String) */ @Override public void addPrefixMapping(String prefix, String uri) { if (fNamespacePrefixMappings == null) { fNamespacePrefixMappings = new HashMap<>(); } fNamespacePrefixMappings.put(prefix, uri); } private String getPrefixMapping(String prefix) { if (fNamespacePrefixMappings != null) { return fNamespacePrefixMappings.get(prefix); } return null; } private String getUserPrefixMapping(String prefix) { if (fNamespacePrefixMappings != null) { Set<Entry<String, String>> entrySet = fNamespacePrefixMappings.entrySet(); Iterator<Entry<String, String>> entries = entrySet.iterator(); while (entries.hasNext()) { Map.Entry<String, String> entry = entries.next(); if (entry.getValue().equals(prefix)) { return entry.getKey(); } } } return null; } /** * Compute the encoding for the backing build file * * @since 3.7 */ void computeEncoding() { try { IFile file = getFile(); if (file != null) { fEncoding = getFile().getCharset(true); return; } } catch (CoreException e) { // do nothing. default to UTF-8 } // try the file buffer manager - likely an external file IPath path = getLocationProvider().getLocation(); if (path != null) { File buildfile = path.toFile(); try (FileReader reader = new FileReader(buildfile)) { QualifiedName[] options = new QualifiedName[] { IContentDescription.CHARSET }; IContentDescription desc = Platform.getContentTypeManager().getDescriptionFor(reader, buildfile.getName(), options); if (desc != null) { fEncoding = desc.getCharset(); return; } } catch (IOException ioe) { // do nothing } } fEncoding = IAntCoreConstants.UTF_8; } /* * (non-Javadoc) * * @see org.eclipse.ant.internal.ui.model.IAntModel#getEncoding() */ @Override public String getEncoding() { return fEncoding; } }